package model;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedList;

import controller.ACSystem;

/**
 * Contains some of the backbone of the AuctionCentral System including keeping
 * track of the calendar functions and the business rules that determine if a
 * new auction can be placed on a certain day or not, etc.
 * 
 * @author Kevin
 * @version 1.0
 * @date Winter 2014
 */
public class AuctionCalendar {

	/** Maximum number of live auctions */
	private final int MAX_AUCTIONS = 25;

	/** Maximum number of days in the future */
	private final int MAX_DAYS = 90;

	/** Number of milliseconds in a day */
	// private final long MILLIS_IN_A_DAY = 86400000;

	/** Today's date */
	private Calendar todays_date = Calendar.getInstance();

	/**
	 * Format to be used for displaying the date/time (hh for 12-hour, HH for
	 * 24)
	 */
	private SimpleDateFormat my_format = new SimpleDateFormat(
			"EE dd/MM/yyyy hh:mm aa");

	/**
	 * This list will hold the next 90 auction days and must be used to check if
	 * a date is available or not.
	 */
	private static LinkedList<AuctionDay> my_list = new LinkedList<AuctionDay>();

	/**
	 * This list contains past auctions and expired calendar days.
	 */
	private static LinkedList<AuctionDay> my_history = new LinkedList<AuctionDay>();

	/* ************************************************ */

	/**
	 * Build a new calendar.
	 */
	public AuctionCalendar() {

		// Make sure todays date is accurate
		setTodaysDate();

		// Build the list of days
		for (int i = 0; i < MAX_DAYS; i++) {
			// Adds an item to the list
			// Auction day receives a blank Calendar instance and adds "i" days
			// to it
			my_list.add(new AuctionDay(Calendar.getInstance(), i));
			my_history.add(new AuctionDay(Calendar.getInstance(), -i));
		}
	}

	/* ************************************************ */

	/**
	 * FOR TESTING ONLY! DONT EVER USE IT!
	 * 
	 * @param by_this_much
	 */
	public void screwUpDate(int by_this_much) {
		// simulates the passage of time, changes current hour "by this much"
		todays_date.add(Calendar.HOUR, by_this_much);
	}

	/**
	 * 
	 * WARNING: will return an item even if the auction is over Searches ALL
	 * auctions and all their items for the requested item
	 * 
	 * @pre item_number exists
	 * @post Item that matches the item number will return
	 * 
	 * @param item_number
	 *            Search for this item
	 * @return Item
	 */
	public Item findItem(int item_number) {
		Item result = null;
		boolean is_found = false;
		// Search all the days in the current auction list
		for (int i = 0; i < my_list.size(); i++) {
			// Search each day for auction 1
			if (my_list.get(i).getAuction1() != null) {
				result = my_list.get(i).getAuction1().findItem(item_number);
				if (result != null) {
					// Exit when found
					is_found = true;
					break;
				}
			}
			// Search each day for auction 2
			if (my_list.get(i).getAuction2() != null) {
				result = my_list.get(i).getAuction2().findItem(item_number);
				if (result != null) {
					// Exit when found
					is_found = true;
					break;
				}
			}
		}
		// If not found in current auctions, check history
		if (!is_found) {
			for (int i = 0; i < my_history.size(); i++) {
				// Check each history day auction 1
				if (my_history.get(i).getAuction1() != null) {
					result = my_history.get(i).getAuction1()
							.findItem(item_number);
					if (result != null) {
						// Exit if found
						is_found = true;
						break;
					}
				}
				// Check each history day auction 2
				if (my_history.get(i).getAuction2() != null) {
					result = my_history.get(i).getAuction2()
							.findItem(item_number);
					if (result != null) {
						// Exit if found
						is_found = true;
						break;
					}
				}
			}
		}
		return result;
	}

	/**
	 * Finds an auction by Donyina-format name
	 * 
	 * @pre the_name is EXACTLY correct
	 * @param the_name
	 *            Example: Organization-Name-Monthnumber-Daynumber-Year
	 * @return
	 */
	public Auction findAuction(String the_name) {
		Auction result = null;
		boolean found = false;
		for (int i = 0; i < my_list.size(); i++) {
			if (my_list.get(i).getAuction1() != null) {
				if (my_list.get(i).getAuction1().getName().equals(the_name)) {
					result = my_list.get(i).getAuction1();
					found = true;
					break;
				}
			}
			if (my_list.get(i).getAuction2() != null) {
				if (my_list.get(i).getAuction2().getName().equals(the_name)) {
					result = my_list.get(i).getAuction2();
					found = true;
					break;
				}
			}
		}
		if (!found) {
			for (int i = 0; i < my_history.size(); i++) {
				if (my_history.get(i).getAuction1() != null) {
					if (my_history.get(i).getAuction1().getName()
							.equals(the_name)) {
						result = my_history.get(i).getAuction1();
						found = true;
						break;
					}
				}
				if (my_history.get(i).getAuction2() != null) {
					if (my_history.get(i).getAuction2().getName()
							.equals(the_name)) {
						result = my_history.get(i).getAuction2();
						found = true;
						break;
					}
				}
			}
		}
		return result;
	}

	/**
	 * True if the requested Date is a valid day to add another auction NOTE:
	 * Does not check to see if the nonprofit already has a current auction as
	 * that has nothing to do with "date"
	 * 
	 * @param the_date
	 *            the Date requested
	 * @return true/false
	 */
	public boolean isValidDate(final Date the_date) {
		boolean result = false;
		setTodaysDate(); // make sure today's date is accurate
		Calendar temp = Calendar.getInstance();
		temp.setTime(todays_date.getTime()); // set temp to today's date

		if (the_date.before(temp.getTime())) { // requested date is before
												// current date
			result = false;
		} else {
			int c = 0; // counter
			while (the_date.after(temp.getTime())) {
				c++; // increment counter
				temp.add(Calendar.DAY_OF_MONTH, 1); // increment temp date
			}
			if (c > 90) {
				result = false; // requested date is too far in advance
			} else if (moreThanFivePerSeven(the_date)) {
				result = false;
			} else {
				result = !my_list.get(c).isFull(); // requested date is full?
			}
		}

		return result;
	}

	@SuppressWarnings("deprecation")
	public boolean moreThanFivePerSeven(Date d) {
		boolean result = false;
		LinkedList<AuctionDay> temp = new LinkedList<AuctionDay>();
		int days_from_now = 0;
		for (int i = 0; i < my_list.size(); i++) {
			if (my_list.get(i).getDate().getTime().getDate() != d.getDate()
					&& my_list.get(i).getDate().getTime().getMonth() != d
							.getMonth()) {
				days_from_now++;
			} else {
				break;
			}
		}
		int count = 0;
		if (days_from_now < 7) {
			// Get days that have passed from history list
			for (int i = 1; i < 7 - days_from_now; i++) {
				temp.add(my_history.get(i - 1));
			}
			// Get days in the future including today
			for (int i = 0; i <= days_from_now; i++) {
				temp.add(my_list.get(i));
			}
		} else {
			for (int i = days_from_now - 7; i <= days_from_now; i++) {
				for (int j = i; j <= i + 7; j++) {
					if (my_list.get(j).getAuction1() != null) {
						count++;
					}
					if (my_list.get(j).getAuction2() != null) {
						count++;
					}
				}
				if (count >= 5) {
					result = true;
					break;
				} else {
					count = 0;
				}
			}
		}

		if (!result) {
			for (int i = 0; i < temp.size(); i++) {
				if (temp.get(i).getAuction1() != null) {
					count++;
				}
				if (temp.get(i).getAuction2() != null) {
					count++;
				}
			}
			if (count >= 5) {
				result = true;
			}
		}
		return result;
	}

	/**
	 * Checks if the list is full
	 * 
	 * @return
	 */
	public boolean isFull() {
		boolean result = false;
		if (ACSystem.getMy_auctions().size() >= MAX_AUCTIONS) {
			result = true;
		}
		return result;
	}

	/**
	 * Add a new auction to the list
	 * 
	 * @param add_me
	 *            the Auction to be added
	 */
	@SuppressWarnings("deprecation")
	public int addAuction(Auction add_me) {
		int error = 0;
		Calendar temp = Calendar.getInstance(); // temp calendar to compare
		int c = 0; // counter
		if (isValidDate(add_me.getDate())) {
			while (temp.get(Calendar.DAY_OF_MONTH) != add_me.getDate()
					.getDate()) { // deprecated but we can't help it
				temp.add(Calendar.DAY_OF_MONTH, 1);
				c++;
			}
			if (ACSystem.getMy_auctions().size() < MAX_AUCTIONS) {
				my_list.get(c).addAuction(add_me); // add to the correct day
				ACSystem.addAuction(add_me);

			} else
				error = -1;
		} else {
			error = -1;
		}
		return error;
	}

	/**
	 * @return the todays_date
	 */
	public Date getTodaysDate() {
		setTodaysDate();
		return todays_date.getTime();
	}

	/**
	 * Takes in a Date and returns the corresponding AuctionDay
	 * 
	 * @param the_date
	 * @return
	 */
	@SuppressWarnings("deprecation")
	public static AuctionDay getAuctions(final Date the_date) {
		AuctionDay result = null;
		if (the_date.before(new Date())) {
			for (int i = 0; i < my_history.size(); i++) {
				if (the_date.getMonth() == my_history.get(i).getDate()
						.get(Calendar.MONTH)
						&& the_date.getDate() == my_history.get(i).getDate()
								.get(Calendar.DAY_OF_MONTH)) {
					result = my_history.get(i);
					break;
				}
			}
		} else {
			for (int i = 0; i < my_list.size(); i++) {
				if (the_date.getMonth() == my_list.get(i).getDate()
						.get(Calendar.MONTH)
						&& the_date.getDate() == my_list.get(i).getDate()
								.get(Calendar.DAY_OF_MONTH)) {
					result = my_list.get(i);
					break;
				}
			}
		}
		return result;
	}

	/**
	 * Updates the current day and moves completed days to the history
	 * 
	 * @post Today's date will be set correctly
	 * @post my_list will be updated
	 * @post my_history will be updated
	 * @param todays_date
	 *            the todays_date to set
	 */
	public void setTodaysDate() {
		Calendar temp = Calendar.getInstance(); // temp variable of the current
												// time
		int c = 0; // counter (if needed)
		if (temp.get(Calendar.DAY_OF_MONTH) != todays_date
				.get(Calendar.DAY_OF_MONTH)) {
			// date has changed, find out by how much
			while (temp.get(Calendar.DAY_OF_MONTH) != todays_date
					.get(Calendar.DAY_OF_MONTH)) {
				temp.add(Calendar.DAY_OF_MONTH, -1); // decrement temp date
				c++; // count
			}
			for (int i = 1; i <= c; i++) {
				AuctionDay a = my_list.removeFirst();
				my_history.add(a); // take the first day off of the list and add
									// it to history
				my_list.add(new AuctionDay(Calendar.getInstance(), MAX_DAYS)); // add
																				// a
																				// new
																				// day
																				// to
																				// the
																				// end

			}
		}
		this.todays_date.setTime(new Date()); // update current date
	}

	public boolean hasAuction(final NonProfit the_non_profit) {

		boolean result = false;
		for (int i = 0; i < my_list.size(); i++) {
			if (my_list.get(i).getAuction1() != null) {
				if (my_list.get(i).getAuction1().getOwner() == the_non_profit) {
					result = true;
					break;
				}
			}
			if (my_list.get(i).getAuction2() != null) {
				if (my_list.get(i).getAuction2().getOwner() == the_non_profit) {
					result = true;
					break;
				}
			}
		}
		for (int i = 0; i < my_history.size(); i++) {
			if (my_history.get(i).getAuction1() != null) {
				if (my_history.get(i).getAuction1().getOwner() == the_non_profit) {
					result = true;
					break;
				}
			}
			if (my_history.get(i).getAuction2() != null) {
				if (my_history.get(i).getAuction2().getOwner() == the_non_profit) {
					result = true;
					break;
				}
			}
		}

		return result;
	}

	/**
	 * @return the MAX_AUCTIONS
	 */
	public int getMaxAuctions() {
		return MAX_AUCTIONS;
	}

	/**
	 * @return the my_format
	 */
	public SimpleDateFormat getMyFormat() {
		return my_format;
	}

	/**
	 * @return the my_list
	 */
	public static LinkedList<AuctionDay> getList() {
		return my_list;
	}

	public static boolean isTimeValid(TimeObject the_auction_time, Date the_date) {
		boolean result = false;

		TimeObject tempTime = the_auction_time;
		int temp_start_hour = tempTime.getStartHour();
		int temp_end_hour = temp_start_hour + tempTime.getDuration();
		AuctionDay the_auction_day = getAuctions(the_date);

		if (the_auction_day.isFull()) {
			result = false;
		} else if (the_auction_day.is_empty) {
			result = true;
		} else {
			TimeObject a1_time = the_auction_day.getAuction1()
					.getStartTimeObject();
			int a1_start_hour = a1_time.getStartHour();
			int a1_end_hour = a1_start_hour + a1_time.getDuration();

			if (a1_start_hour < temp_start_hour) {
				if ((a1_end_hour + 2) > temp_start_hour) {
					result = false;
				}else{
					result = true;
				}
			} else {
				if ((temp_end_hour + 2) > a1_start_hour) {
					result = false;
				}else{
					result=true;
				}
			}

		}
		return result;
	}
}
